実践!AWS CDK #12 NAT ゲートウェイ
はじめに
NAT ゲートウェイ作ります!
前回の記事はこちら。
AWS 構成図
前回作成した Elastic IP を使用して、パブリックサブネットに配置する NAT ゲートウェイを作成していきます。
設計
NAT ゲートウェイのプロパティはこちら。
リソース名 | サブネット | Elastic IP |
---|---|---|
devio-stg-ngw-1a | devio-stg-subnet-public-1a | devio-stg-eip-ngw-1a |
devio-stg-ngw-1c | devio-stg-subnet-public-1c | devio-stg-eip-ngw-1c |
2 つの NAT ゲートウェイを作成します。
サブネットと Elastic IP はそれぞれ以前作成したものを設定します。
実装
NAT ゲートウェイに関する処理を行うクラスはこちら。
import * as cdk from '@aws-cdk/core'; import { CfnNatGateway, CfnSubnet, CfnEIP } from '@aws-cdk/aws-ec2'; import { Resource } from './abstract/resource'; interface ResourceInfo { readonly id: string; readonly resourceName: string; readonly allocationId: () => string; readonly subnetId: () => string; readonly assign: (natGateway: CfnNatGateway) => void; } export class NatGateway extends Resource { public ngw1a: CfnNatGateway; public ngw1c: CfnNatGateway; private readonly subnetPublic1a: CfnSubnet; private readonly subnetPublic1c: CfnSubnet; private readonly elasticIpNgw1a: CfnEIP; private readonly elasticIpNgw1c: CfnEIP; private readonly resourcesInfo: ResourceInfo[] = [ { id: 'NatGateway1a', resourceName: 'ngw-1a', allocationId: () => this.elasticIpNgw1a.attrAllocationId, subnetId: () => this.subnetPublic1a.ref, assign: natGateway => this.ngw1a = natGateway }, { id: 'NatGateway1c', resourceName: 'ngw-1c', allocationId: () => this.elasticIpNgw1c.attrAllocationId, subnetId: () => this.subnetPublic1c.ref, assign: natGateway => this.ngw1c = natGateway } ]; constructor( subnetPublic1a: CfnSubnet, subnetPublic1c: CfnSubnet, elasticIpNgw1a: CfnEIP, elasticIpNgw1c: CfnEIP ) { super(); this.subnetPublic1a = subnetPublic1a; this.subnetPublic1c = subnetPublic1c; this.elasticIpNgw1a = elasticIpNgw1a; this.elasticIpNgw1c = elasticIpNgw1c; }; createResources(scope: cdk.Construct) { for (const resourceInfo of this.resourcesInfo) { const natGateway = this.createNatGateway(scope, resourceInfo); resourceInfo.assign(natGateway); } } private createNatGateway(scope: cdk.Construct, resourceInfo: ResourceInfo): CfnNatGateway { const natGateway = new CfnNatGateway(scope, resourceInfo.id, { allocationId: resourceInfo.allocationId(), subnetId: resourceInfo.subnetId(), tags: [{ key: 'Name', value: this.createResourceName(scope, resourceInfo.resourceName) }] }); return natGateway; } }
NAT ゲートウェイ生成時には allocationId
と subnetId
を指定する必要があります。これらは動的に取得したいため、ResourceInfo
インタフェースで関数として定義しています。
allocationId: () => this.elasticIpNgw1a.attrAllocationId, subnetId: () => this.subnetPublic1a.ref,
resourcesInfo
でこのように指定することで、各変数から getter のように取得できます。
なお、allocationId は CfnEIP インスタンスの attrAllocationId
で取得しなければなりません。ref
で取得できる値は IP アドレス となるのでご注意ください。(ハマリポイントです)
メインのプログラムはこちら。
ハイライト部分を追記しました。
import * as cdk from '@aws-cdk/core'; import { Vpc } from './resource/vpc'; import { Subnet } from './resource/subnet'; import { InternetGateway } from './resource/internetGateway'; import { ElasticIp } from './resource/elasticIp'; import { NatGateway } from './resource/natGateway'; export class DevioStack extends cdk.Stack { constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); // VPC const vpc = new Vpc(); vpc.createResources(this); // Subnet const subnet = new Subnet(vpc.vpc); subnet.createResources(this); // Internet Gateway const internetGateway = new InternetGateway(vpc.vpc); internetGateway.createResources(this); // Elastic IP const elasticIp = new ElasticIp(); elasticIp.createResources(this); // NAT Gateway const natGateway = new NatGateway( subnet.public1a, subnet.public1c, elasticIp.ngw1a, elasticIp.ngw1c ); natGateway.createResources(this); } }
今回はコンストラクタに渡す引数が多めです。しかし、このように書くことで関連するリソースがわかりやすくなっていますね。
テスト
テストコードはこちら。
import { expect, countResources, haveResource } from '@aws-cdk/assert'; import * as cdk from '@aws-cdk/core'; import * as Devio from '../../lib/devio-stack'; test('NatGateway', () => { const app = new cdk.App(); const stack = new Devio.DevioStack(app, 'DevioStack'); expect(stack).to(countResources('AWS::EC2::NatGateway', 2)); expect(stack).to(haveResource('AWS::EC2::NatGateway', { Tags: [{ 'Key': 'Name', 'Value': 'undefined-undefined-ngw-1a' }] })); expect(stack).to(haveResource('AWS::EC2::NatGateway', { Tags: [{ 'Key': 'Name', 'Value': 'undefined-undefined-ngw-1c' }] })); });
以下を確認しています。
- NAT ゲートウェイリソースが 2 つあること
- 各 NAT ゲートウェイのリソース名が正しいこと
確認
マネジメントコンソール上でリソースを確認してみましょう。
きちんと作成されていますね。
CloudFormation 版
今回のコードを CFn で書くと以下のようになります。
NatGateway1a: Type: AWS::EC2::NatGateway Properties: AllocationId: Fn::GetAtt: - ElasticIpNgw1a - AllocationId SubnetId: Ref: SubnetPublic1a Tags: - Key: Name Value: devio-stg-ngw-1a NatGateway1c: Type: AWS::EC2::NatGateway Properties: AllocationId: Fn::GetAtt: - ElasticIpNgw1c - AllocationId SubnetId: Ref: SubnetPublic1c Tags: - Key: Name Value: devio-stg-ngw-1c
GitHub
今回のソースコードは コチラ です。
おわりに
これでインターネットにアクセスする口はできました。
ネットワークに関して作成したいリソースはあと 2 つ。
- ルートテーブル
- ネットワーク ACL
順に作っていきましょう。